﻿-- Raid Roster information
--
-- This builds two arrays, one for raid/party members
-- one for any mobs targeted by raid/party members
--
-- These are only generated once per update then every other
-- window can just refer to these as needed

-- The arrays
OZ_RaidRoster = {}
OZ_PlayerParty = nil
OZ_RaidNames = {}

-- Store them here so we don't need to build any strings dynamically
local OZ_PLAYER_ID = "player"
local OZ_PLAYER_TARGET = "playertarget"
local OZ_PLAYER_PET = "pet"
local OZ_PARTY_IDS = { "party1","party2","party3","party4","party5" }
local OZ_PARTYTARGET_IDS = { "party1target","party2target","party3target","party4target","party5target" }
local OZ_PARTYPET_IDS = { "party1pet","party2pet","party3pet","party4pet","party5pet" }
local OZ_PARTYPETTARGET_IDS = { "party1pettarget","party2pettarget","party3pettarget","party4pettarget","party5pettarget" }
local OZ_RAID_IDS = { "raid1","raid2","raid3","raid4","raid5","raid6","raid7","raid8","raid9","raid10",
				"raid11","raid12","raid13","raid14","raid15","raid16","raid17","raid18","raid19","raid20",
				"raid21","raid22","raid23","raid24","raid25","raid26","raid27","raid28","raid29","raid30",
				"raid31","raid32","raid33","raid34","raid35","raid36","raid37","raid38","raid39","raid40" }
local OZ_RAIDTARGET_IDS = { "raid1target","raid2target","raid3target","raid4target","raid5target","raid6target","raid7target","raid8target","raid9target","raid10target",
					  "raid11target","raid12target","raid13target","raid14target","raid15target","raid16target","raid17target","raid18target","raid19target","raid20target",
					  "raid21target","raid22target","raid23target","raid24target","raid25target","raid26target","raid27target","raid28target","raid29target","raid30target",
					  "raid31target","raid32target","raid33target","raid34target","raid35target","raid36target","raid37target","raid38target","raid39target","raid40target" }


-- Functions in here:
-- OZ_SetupRaidRoster()		- Initialising the data
-- OZ_UpdateRoster()		- Fill in current raid members
-- OZ_UpdateTargets()		- Find all unique targets

-- register & handle these events instead:
--RAID_ROSTER_UPDATE
--RAID_TARGET_UPDATE - Icons
--PARTY_MEMBER_DISABLE - offline/dead
--PARTY_MEMBER_ENABLE - online/rezzed
--PARTY_MEMBERS_CHANGED - raid groups changed
--PARTY_LEADER_CHANGED
--UNIT_AURA			- Buff/debuff
--UNIT_HEALTH
--UNIT_MANA
--UNIT_MAXHEALTH
--UNIT_MAXMANA

local function print(text)
	DEFAULT_CHAT_FRAME:AddMessage("|c0033CCFF"..text);
end

OZ_ICON_LIST = {
					"Interface\\AddOns\\OzRaid\\star",
					"Interface\\AddOns\\OzRaid\\circle",
					"Interface\\AddOns\\OzRaid\\diamond",
					"Interface\\AddOns\\OzRaid\\triangle",
					"Interface\\AddOns\\OzRaid\\moon",
					"Interface\\AddOns\\OzRaid\\square",
					"Interface\\AddOns\\OzRaid\\cross",
					"Interface\\AddOns\\OzRaid\\skull",
					"Interface\\GroupFrame\\UI-Group-LeaderIcon",
					"Interface\\GroupFrame\\UI-Group-MasterLooter",
					"Interface\\GroupFrame\\UI-Group-AssistantIcon",
				};

OZ_NAME_FORMATS = {
						[1] = { name = "Plain",		string="%s"		},
						[2] = { name = "Name (n)",	string="%s (%d)"},
						[3] = { name = "Name n",	string="%s %d"	},
						[4] = { name = "Name..n",	string="%s..%d"	},
				};

function OZ_SetupRaidRoster()
	local x

	OZ_RaidRoster = {
		["nMembers"]	= 0,
		["member"]		= {},
		["nTargets"]	= 0,
		["target"]		= {},
	}

	for x = 1 , 80 do
		OZ_RaidRoster.member[x] = {
			["name"]		= "name"..x,
			["unit"]		= "player",
			["rank"]		= 0,
			["subgroup"]	= 0,
			["level"]		= 0,
			["class"]		= "Mage",
			["fileName"]	= "MAGE",
			["zone"]		= "Offline",
			["online"]		= nil,
			["isDead"]		= nil,

			["unit"]		= "player",
			["colour"]		= {["r"]=1.0, ["g"]=0.0, ["b"]=0.0, ["a"]=1.0},

			["range"]		= nil,
			["health"]		= 0,
			["maxHealth"]	= 1000,
			["mana"]		= 0,
			["maxMana"]		= 1000,
			["offline"]		= nil,
		};
	end
	
	for x = 1 , 10 do
		OZ_RaidRoster.target[x] = {
			["name"]		= "name"..x,
			["unit"]		= "playertarget",
		};
	end
end

OZ_LOCALISED_CLASSES = { ["Pet"] = "Pet" };

function OzGetLocalisedClass(fileName)
	if not OZ_LOCALISED_CLASSES[fileName] then
		return fileName
	end
	return OZ_LOCALISED_CLASSES[fileName]
end

function OzSetFormattedNames(dest)
	if not dest.formatted then
		dest.formatted = {}
	end
	local i
	for i=1,4 do
		dest.formatted[i] = string.format( OZ_NAME_FORMATS[i].string, dest.name,dest.subgroup );
	end
end


-- Fill in bar data for window 'n'
function OZ_GetMemberData(n, unit, target, pet)
	if not unit or not UnitExists(unit) then
		return
	end
	local dest = OZ_RaidRoster.member[n]
	local class,fileName = UnitClass(unit)
	if fileName then
		dest.class		= class
		dest.fileName	= fileName
		if not OZ_LOCALISED_CLASSES[fileName] then
			OZ_LOCALISED_CLASSES[fileName] = class
		end
	else
		dest.class		= "<none>"
		dest.fileName	= "<none>"
	end
--print("OzRaid: class="..dest.class..", fName="..dest.fileName);
	dest.unit 		= unit
	dest.target		= target
	dest.pet		= pet
    dest.petClass   = nil
	dest.name		= UnitName(unit)
	dest.rank		= 0
	dest.subgroup	= 1
	dest.level		= UnitLevel(unit)
	dest.zone		= nil
	dest.online		= UnitIsConnected(unit)
	dest.isDead		= UnitIsDeadOrGhost(unit)
	dest.power		= UnitPowerType(unit)

	OzSetFormattedNames(dest)

	OZ_RaidNames[dest.name] = n

	OZ_SetExtraMemberData(n)
end

function OZ_AddPetData(i)
	local unit = OZ_RaidRoster.member[i] 
	local pet = unit.pet
	if not UnitExists(pet) then
		return
	end
	local n = OZ_RaidRoster.nMembers + 1
	
	local dest		= OZ_RaidRoster.member[n]
	dest.unit 		= pet
	dest.target		= pet.."target"
	dest.name		= UnitName(pet)
	dest.rank		= 0
	dest.subgroup	= unit.subgroup
	dest.level		= UnitLevel(pet)
	dest.class		= "Pet"
	dest.fileName	= "PET"
    dest.petClass   = unit.fileName.."PET"
	dest.zone		= nil
	dest.online		= UnitIsConnected(pet)
	dest.isDead		= UnitIsDeadOrGhost(pet)
	dest.power		= UnitPowerType(pet)
	OzSetFormattedNames(dest)

	OZ_RaidRoster.nMembers = n
	OZ_RaidNames[dest.name] = n

	OZ_SetExtraMemberData(n)

	return 1
end

function OzRangeTest(unit)
	-- test for 40 yards 'range' if player is a healer & target is friendly
	if UnitExists(unit) then
        if CheckInteractDistance(unit, 1) then
            return 2
        elseif OzPlayerHealSpell and UnitIsFriend(unit,OZ_PLAYER_ID) and UnitIsVisible(unit) then
			if IsSpellInRange( OzPlayerHealSpell, unit ) == 1 then
				return 1
			end
		end
		return CheckInteractDistance(unit, 4)
	end
end

function OZ_SetExtraMemberData(n)
	local m, c
	local dest = OZ_RaidRoster.member[n]
	local unit = dest.unit
	dest.isDead		= UnitIsDeadOrGhost(unit)
	dest.online		= UnitIsConnected(unit)
    OZ_UpdateHealth( dest )
	dest.range	= OzRangeTest(unit)

	if not UnitIsAFK(unit) then
		dest.afk = nil
	elseif not dest.afk then
		dest.afk = GetTime()
	end

    if not dest.isDead then
        dest.dead = nil;
    elseif not dest.dead then
        dest.dead = GetTime();
    end

	if(dest.online)then
		dest.offline = nil
	elseif(not dest.offline)then
		dest.offline = GetTime()
	end

	if(dest.rank == 2)then
		dest.icon = OZ_ICON_LIST[9]
	elseif(dest.rank == 1)then
		dest.icon = OZ_ICON_LIST[11]
	else
		dest.icon = nil
	end
	local rt = GetRaidTargetIndex(unit)
	dest.iconVal = rt
	if(rt)then
		dest.icon = OZ_ICON_LIST[rt]
	end
    
    -- If we are in a ready check update, get status
    if OZ_ReadyCheckActive == 1 then
        -- CT Ready check
        local stats;
        if CT_RA_Stats then 
            stats = CT_RA_Stats[dest.name]
            if stats and not stats["notready"] then
                stats = nil
            end
        end
        local rc = GetReadyCheckStatus(unit)
        if rc=='notready' or (stats and stats["notready"] == 2) then
            dest.isReady = 0;
        elseif rc=='ready' or (stats and not stats["notready"]) then
            dest.isReady = 1;
        else
            dest.isReady = nil;
        end
    end

end


function OZ_UpdateRoster()
	local i, unit
	OZ_PlayerParty = nil
	OZ_RaidRoster.inParty = nil
	OZ_RaidRoster.solo = nil
    OZ_RaidRoster.isPromoted = nil   -- Raid/group rank

	local nMembers = GetNumRaidMembers()
	if( nMembers == 0 ) then

		-- Not a raid - either lone or in party
		-- Add ourselves...
		OZ_PlayerParty = 1
		OZ_RaidRoster.nMembers = 1
		OZ_GetMemberData(OZ_RaidRoster.nMembers,OZ_PLAYER_ID,OZ_PLAYER_TARGET,OZ_PLAYER_PET)

		if(UnitIsPartyLeader("player")) then
			OZ_RaidRoster.member[1].rank = 2
            OZ_RaidRoster.isPromoted = 1
		end
		OZ_RaidRoster.solo = 1

		nMembers = GetNumPartyMembers()
		if(nMembers > 0) then
			OZ_RaidRoster.inParty = 1
			OZ_RaidRoster.solo = nil
			for i = 1,nMembers do
				OZ_RaidRoster.nMembers = OZ_RaidRoster.nMembers + 1
				OZ_GetMemberData(OZ_RaidRoster.nMembers, OZ_PARTY_IDS[i], OZ_PARTYTARGET_IDS[i], OZ_PARTYPET_IDS[i])
				if(UnitIsPartyLeader(OZ_PARTY_IDS[i])) then
					OZ_RaidRoster.member[OZ_RaidRoster.nMembers].rank = 2
				end
			end
		end
	else
		OZ_RaidRoster.nMembers = 0;
		for i = 1,40 do
			local name,rank,subgroup,level,class,fileName,zone,online,isDead = GetRaidRosterInfo(i)
			if(name) then
				OZ_RaidRoster.nMembers = OZ_RaidRoster.nMembers + 1
                
				if fileName and not OZ_LOCALISED_CLASSES[fileName] then
					OZ_LOCALISED_CLASSES[fileName] = class
				end
              
				local dest = OZ_RaidRoster.member[OZ_RaidRoster.nMembers]
				OZ_RaidNames[dest.name] = i
				dest.unit		= OZ_RAID_IDS[i]
				dest.target		= OZ_RAIDTARGET_IDS[i]
				dest.pet		= OZ_RAID_IDS[i].."pet"
                
                -- Strip off server names (PvP)
                local s,e = string.find(name,'-')
                if s then
                    name = string.sub(name,1,s-1)
                end
                
				dest.name		= name
				dest.rank		= rank
				dest.subgroup	= subgroup
				dest.level		= level
				dest.class		= class
                dest.petClass   = nil
				dest.fileName	= fileName
				dest.zone		= zone
				dest.online		= online
				dest.isDead		= isDead
				dest.power		= UnitPowerType(dest.unit)
				OzSetFormattedNames(dest)

				OZ_SetExtraMemberData(OZ_RaidRoster.nMembers)

				if( UnitIsUnit(OZ_PLAYER_ID, dest.unit) )then
                    if rank > 0 then
                        OZ_RaidRoster.isPromoted = true
                    end
					OZ_PlayerParty = subgroup
				end

				-- Check CTMod MT array for MT assignments
				local key,val
				dest.ctmt = nil
				local tankList = GetMainTankList()
				if tankList then
					for key,val in pairs(tankList) do
						if val == name then
							dest.ctmt = key
							break
						end
					end
				end

			end
		end
	end
	-- Add pets to the end (reduces errors for summoning in combat)
	for i = 1,OZ_RaidRoster.nMembers do
		OZ_AddPetData(i)
	end

end

OZ_TargetListUpdate = 0;

function OZ_UpdateTargets()
    if OZ_CurrentTime == OZ_TargetListUpdate then return end
    OZ_TargetListUpdate = OZ_CurrentTime;
	local i,j
	OZ_RaidRoster.nTargets = 0;
	local m = OZ_RaidRoster.nMembers
	for i = 1,m do
		local player = OZ_RaidRoster.member[i]
		player.hasAggro = nil
		OZ_UpdateTargetData( player.unit )
	end

	local t = OZ_RaidRoster.nTargets
	if(t>0)then
		for j = 1,t do
			local mob = OZ_RaidRoster.target[j]
			if(mob and mob.unit)then
				local targetted = mob.target
				mob.targetMember = nil
				if(UnitExists(targetted))then
					for i= 1,m do
						local dest = OZ_RaidRoster.member[i]
						if(UnitIsUnit(targetted,dest.unit))then
							if(dest.hasAggro)then
								dest.hasAggro = 1
							else
								dest.hasAggro = 1
							end
							mob.targetMember = i
							break
						end
					end
				end
			end
		end
	end
end

function OZ_UpdateTargetData(unit)
	local t = unit.."target"
	if( UnitExists(t) and
		(UnitCanAttack("player",t)) and
		(not UnitIsDead(t)) ) then

		-- We have a valid target
		-- check if we have already counted this one...
		if( OZ_RaidRoster.nTargets > 0 ) then
			for n = 1,OZ_RaidRoster.nTargets do
				if( UnitIsUnit( t, OZ_RaidRoster.target[n].unit ) ) then
					return
				end
			end
		end

		-- Ok, this target is a mob, and is notyet in the list
		OZ_RaidRoster.nTargets = OZ_RaidRoster.nTargets + 1
		local dest = OZ_RaidRoster.target[OZ_RaidRoster.nTargets]
		if(not dest)then
			OZ_RaidRoster.target[OZ_RaidRoster.nTargets] = {}
			dest = OZ_RaidRoster.target[OZ_RaidRoster.nTargets]
		end
		dest.unit		= t
		dest.target		= t.."target"
		dest.name		= UnitName(t)
		dest.level		= UnitLevel(t)
		dest.rank		= 0
		dest.subgroup	= 0
		dest.class		= "Mob"
        dest.petClass   = nil
		dest.fileName	= "TARGET"
		dest.zone		= nil
		dest.online		= 1
		dest.isDead		= UnitIsDead(t)
        OZ_UpdateHealth( dest )
	end
end

function OZ_UpdateHealth( dest )
    if OZ_CurrentTime == dest.updateTime then return end
    dest.updateTime = OZ_CurrentTime;
    local m, c
    local t = dest.unit
    if not dest.isDead and dest.online then
        m,c = UnitManaMax( t ),UnitMana( t )
        dest.mana		= c
        dest.maxMana	= m

        m,c = UnitHealthMax( t ),UnitHealth( t )
        dest.health		= c
        dest.maxHealth	= m

        local rt = GetRaidTargetIndex(t)
        dest.iconVal = rt
        if(rt)then
            dest.icon = OZ_ICON_LIST[rt]
        else
            dest.icon = nil
        end
    else
        dest.health		= 0
        dest.maxHealth	= 5000
        dest.mana	= 0
        dest.maxMana	= 5000
    end
    dest.range	= CheckInteractDistance(t, 4)
    if( (dest.range) and (CheckInteractDistance(t, 1)) )then
        dest.range = 2
    end
end